﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Bouyei.Geo.Geometries
{
    using GeoParser;

    public class Geometry : IGeometry
    {
        public GeoType GeometryType { get; set; }

        private List<List<GeoSequence>> Geometric { get;  set; }

        public  int GeometryCount { get { return Geometric.Count; } }

        public GeoSequence GetSequence(int geoIndex,int seqIndex)
        {
            return Geometric[geoIndex][seqIndex];
        }

        public List<GeoSequence>GetGemoetry(int geoIndex)
        {
            return Geometric[geoIndex];
        }

        public Geometry(GeoType geometryType, GeoSequence sequence)
        {
            this.GeometryType = geometryType;
            Geometric = new List<List<GeoSequence>>(1);
            Geometric.Add(new List<GeoSequence>() {
            sequence
            });
        }

        public Geometry(GeoType geometryType, List<GeoSequence> sequences)
        {
            this.GeometryType = geometryType;
            Geometric = new List<List<GeoSequence>>(1);
            Geometric.Add(sequences);
        }

        public Geometry(GeoType geometryType, List<GeoSequence[]> sequences)
        {
            this.GeometryType = geometryType;
            Geometric = new List<List<GeoSequence>>(sequences.Count);

            foreach (var seqs in sequences)
                Geometric.Add(seqs.ToList());
        }

        public Geometry(GeoType geometryType, string wktString)
        {
            this.GeometryType = geometryType;
            var array = FromWkt(wktString);
            Geometric = new List<List<GeoSequence>>(array.Count);

            if (this.GeometryType == GeoType.POINT)
            {
                Geometric.Add(new List<GeoSequence>() {
                    new GeoSequence( array[0][0])
                });
            }
            else if (this.GeometryType == GeoType.LINESTRING
                || this.GeometryType == GeoType.LineSegment)
            {
                Geometric.Add(new List<GeoSequence>() {
                    new GeoSequence(array[0],this.GeometryType)
                });
            }
            else if (this.GeometryType == GeoType.POLYGON)
            {
                Geometric = new List<List<GeoSequence>>(1);
                Geometric.Add(new List<GeoSequence>(array.Count));

                foreach (var geo in array)
                {
                    Geometric[0].Add(new GeoSequence(geo,this.GeometryType));
                }
            }
            else if (this.GeometryType == GeoType.MULTIPOINT)
            {
                Geometric = new List<List<GeoSequence>>(1);
                Geometric.Add(new List<GeoSequence>(array.Count));
                foreach (var geo in array)
                {
                    Geometric[0].Add(new GeoSequence(geo,this.GeometryType));
                }
            }
            else if (this.GeometryType == GeoType.MULTILINESTRING)
            {
                Geometric = new List<List<GeoSequence>>(1);
                Geometric.Add(new List<GeoSequence>(array.Count));

                foreach (var geo in array)
                {
                    Geometric[0].Add(new GeoSequence(geo,this.GeometryType));
                }
            }else if(this.GeometryType==GeoType.MULTIPOLYGON)
            {
                throw new Exception("not support");
            }
        }

        public Geometry(Coordinate coord)
        {
            this.GeometryType = GeoType.POINT;
            Geometric = new List<List<GeoSequence>>(1);
            Geometric.Add(new List<GeoSequence>(1) {
            new GeoSequence(coord)
            });
        }

        public Geometry(GeoType geometryType,Coordinate[] coords)
        {
            this.GeometryType = geometryType;
            Geometric = new List<List<GeoSequence>>(1);

            Geometric.Add(new List<GeoSequence>() {
             new GeoSequence(coords,geometryType)
            });
        }

        public Geometry(Coordinate[] outer,List<Coordinate[]> interiors)
        {
            this.GeometryType = GeoType.POLYGON;
            Geometric = new List<List<GeoSequence>>(1);

            Geometric.Add(new List<GeoSequence>(interiors.Count + 1));
            Geometric[0].Add(new GeoSequence(outer,this.GeometryType));

            foreach (var hole in interiors)
            {
                Geometric[0].Add(new GeoSequence(hole, this.GeometryType, GeoDirection.AntiClockwise));
            }
        }

        public Geometry(GeoType geometryType,List<Coordinate[]> array)
        {
            this.GeometryType = geometryType;
            Geometric = new List<List<GeoSequence>>(1);
            Geometric.Add(new List<GeoSequence>(array.Count));

            foreach (var geo in array)
            {
                Geometric[0].Add(new GeoSequence(geo,geometryType));
            }
        }

        public Geometry(GeoType geometryType, GeoPoint[] geoPoints)
        {
            this.GeometryType = geometryType;
            Geometric = new List<List<GeoSequence>>(1);
            Geometric.Add(new List<GeoSequence>() {
            new GeoSequence(geoPoints,geometryType)
            });
        }

        public Geometry(GeoType geometryType, GeoLinestring lineString)
        {
            this.GeometryType =geometryType;
            Geometric = new List<List<GeoSequence>>(1);

            Geometric.Add(new List<GeoSequence>(1) { new GeoSequence(lineString, geometryType) });
        }

        public Geometry(GeoLineSegment[] lineSegments)
        {
            this.GeometryType = GeoType.LINESTRING;
            Geometric = new List<List<GeoSequence>>(1);
            Geometric.Add(new List<GeoSequence>(1) { new GeoSequence(lineSegments) });
        }

        public Geometry(GeoLineSegment lineSegment)
        {
            this.GeometryType = GeoType.LineSegment;
            Geometric = new List<List<GeoSequence>>(1);
            Geometric.Add(new List<GeoSequence>(1) {
                new GeoSequence(new GeoPoint[] { lineSegment.Start, lineSegment.End },GeoType.LineSegment)
            });
        }

        public Geometry(GeoPolygon[] polygons)
        {
            this.GeometryType = GeoType.MULTIPOLYGON;
            Geometric =new List<List<GeoSequence>>(polygons.Length);

            foreach (var polygon in polygons)
            {
                Geometric.Add(polygon.Geometric[0]);
            }
        }

        public Geometry(GeoType geometryType,GeoSequence[] sequences)
        {
            this.GeometryType = geometryType;
            Geometric =  new List<List<GeoSequence>>(1);
            Geometric.Add(new List<GeoSequence>(sequences.Length));

            Geometric[0].AddRange(sequences);
        }

        public GeoLineSegment GetBoundrayBox()
        {
            Coordinate min = Geometric[0][0][0].Copy();
            Coordinate max = min.Copy();

            foreach (var geos in Geometric)
            {
                foreach (var geo in geos)
                {
                    for(int i=0;i<geo.Count;++i)
                    {
                        var current = geo[i];
                        if (current > max) max = current;
                        if (current < min) min = current;
                    }
                }
            }
            return new GeoLineSegment(min, max);
        }

        public List<Coordinate[]> FromWkt(string wktString)
        {
            WktParser wkt = new WktParser(wktString);
            return wkt.FromReader();
        }

        public string ToWkt()
        {
            WktParser wkt = new WktParser();
            return wkt.ToWriter(this);
        }

        public bool IsIntersects(Coordinate sMin, Coordinate sMax, Coordinate eMin, Coordinate eMax)
        {
            return !(sMax < eMin || sMin > eMax);
        }
    }
}
